import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } from "axios";

import loginService, { type TokenDTO } from "@/pages/login/service";
import { router } from "@/router";
import { useGlobalStore } from "@/stores/global";
import antdUtils from "@/utils/antd";
import resolveTo from "@/utils/resolve-to";

// 二次刷新使用的 token
const refreshTokenUrl = "/api/auth/refresh/token";

interface RequestQueueItem {
    resolve: any;
    config: InternalAxiosRequestConfig;
    type: "request" | "response";
}

class Request {
    // 实例化数据 创建axios
    private axiosInstance: AxiosInstance;

    // 私有属性 二次token 的状态
    private refreshTokenFlag = false;

    // 私有属性 实际请求次数
    private requestingCount = 0;

    // 私有  最多请求限制
    private limit = 3;

    // 私有  声明 请求队列
    private requestQueue: RequestQueueItem[] = [];

    constructor(config?: CreateAxiosDefaults) {
        // 创建 axios 实例
        this.axiosInstance = axios.create(config);

        // 创建 axios 请求拦截器 并赋予内容
        // 内容修改  拦截器需要返回一个函数 而不是调用
        this.axiosInstance.interceptors.request.use((axiosConfig: InternalAxiosRequestConfig) => this.requestInterceptor(axiosConfig));

        // 创建 axios 响应拦截器 并赋予内容
        this.axiosInstance.interceptors.response.use(
            (response: AxiosResponse<unknown, unknown>) => this.responseSuccessInterceptor(response),
            (error: any) => this.responseErrorInterceptor(error),
        );
    }

    // 请求拦截器内容
    private async requestInterceptor(axiosConfig: InternalAxiosRequestConfig): Promise<any> {
        if ([refreshTokenUrl].includes(axiosConfig.url || "")) {
            return Promise.resolve(axiosConfig);
        }
        // 当前 token 内容存在
        if (this.refreshTokenFlag || this.requestingCount >= this.limit) {
            return new Promise((resolve) => {
                this.requestQueue.push({
                    resolve,
                    config: axiosConfig,
                    type: "request",
                });
            });
        }

        // 请求 计数
        this.requestingCount += 1;
        const { token } = useGlobalStore.getState();

        if (token) {
            axiosConfig.headers.Authorization = `Bearer ${token}`;
        }
        return Promise.resolve(axiosConfig);
    }

    // 响应拦截器 成功的处理
    private async responseSuccessInterceptor(response: AxiosResponse<any, any>): Promise<any> {
        // 当前响应请求的 url 如果 不是 刷新请求
        if (response.config.url !== refreshTokenUrl) {
            // 减少请求实际计数
            this.requestingCount -= 1;
            // 检查当前的消息队列中是否还有未执行的请求
            if (this.requestQueue.length) {
                this.requestByQueue();
            }
        }
    }

    // 刷新 token
    private async refreshToken() {
        const { refreshToken } = useGlobalStore.getState();

        if (!refreshToken) {
            this.toLoginPage();
        }

        // 获取请求 数据
        const [error, data] = await resolveTo<TokenDTO>(loginService.refreshToken(refreshToken));

        if (error) {
            this.toLoginPage();
            return;
        }

        // 存储 token
        useGlobalStore.setState({
            refreshToken: data.refreshToken,
            token: data.token,
        });

        // 清空 二次token 的状态
        this.refreshTokenFlag = false;

        //??? TODO  执行消息队列
        this.requestByQueue();
    }

    // 初始化参数内容
    private reset() {
        this.requestQueue = [];
        this.refreshTokenFlag = false;
        this.requestingCount = 0;
    }

    // 跳转到登录页面
    private toLoginPage() {
        // router
        this.reset();
    }

    private async responseErrorInterceptor(error: any): Promise<any> {
        this.requestingCount -= 1;

        const { config, status } = error?.response || {};
        if (status === 401) {
            return new Promise((resolve) => {
                this.requestQueue.unshift({ resolve, config, type: "response" });

                if (this.refreshTokenFlag) return;
                this.refreshTokenFlag = true;

                this.refreshToken();
            });
        } else {
            console.log("当前行：", 144, "[ antdUtils ] ==>", antdUtils);
            try {
                antdUtils?.notification.error({
                    message: "出错了",
                    description: error?.response?.data?.message,
                });
            } catch (error) {
                console.log("当前行：", 151, "[ error ] ==>", error);
            }
            antdUtils?.notification.error({
                message: "出错了",
                description: error?.response?.data?.message,
            });
            return Promise.reject(error);
        }
    }

    // 执行 消息队列
    private requestByQueue() {
        if (!this.requestQueue.length) return;
        // 最大请求限制 减去 当前请求次数
        Array.from({ length: this.limit - this.requestingCount }).forEach(async () => {
            // 消息队列中第一个进来的内容
            const record: RequestQueueItem = this.requestQueue.shift()!;

            const { config, resolve, type } = record;
            // 响应请求
            if (type === "response") {
                resolve(await this.request(config));
            } else if (type === "request") {
                this.requestingCount += 1;
                const { token } = useGlobalStore.getState();
                config.headers.Authorization = `Bearer ${token}`;
            }
        });
    }

    // 留下口子 最大次数请求
    setLimit(limit: number) {
        this.limit = limit;
    }

    // 发起请求
    request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R> {
        return this.axiosInstance(config);
    }

    get<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
        return this.axiosInstance.get(url, config);
    }

    post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
        return this.axiosInstance.post(url, data, config);
    }

    put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
        return this.axiosInstance.put(url, data, config);
    }

    delete<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
        return this.axiosInstance.delete(url, config);
    }
}

const request = new Request({ timeout: 60 * 100 * 5 });
export default request;
